package com.gzx.plugin.tjzy.modular.sms.api.template;

import cn.hutool.core.collection.CollectionUtil;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.gzx.plugin.tjzy.modular.founds.bo.GzxTransMethodBo;
import com.gzx.plugin.tjzy.modular.founds.constant.FoundsConstant;
import com.gzx.plugin.tjzy.modular.founds.service.GzxFoundsFlowService;
import com.gzx.plugin.tjzy.modular.sms.api.strategy.common.dto.CommonSmsSendReqDto;
import com.gzx.plugin.tjzy.modular.sms.api.strategy.common.dto.CommonSmsSendRespDto;
import com.gzx.plugin.tjzy.modular.sms.batch.entity.GzxSmsDetail;
import com.gzx.plugin.tjzy.modular.sms.batch.enums.GzxSmsSendStatusEnum;
import com.gzx.plugin.tjzy.modular.sms.batch.service.GzxSmsDbService;
import com.gzx.plugin.tjzy.modular.sms.receipt.entity.GzxSmsReceipt;
import com.gzx.plugin.tjzy.modular.sms.receipt.service.GzxSmsReceiptService;
import com.gzx.plugin.tjzy.modular.sms.record.entity.GzxSmsRecord;
import com.gzx.plugin.tjzy.modular.sms.record.enums.GzxSmsSendTypeEnum;
import com.gzx.plugin.tjzy.modular.sms.record.service.GzxSmsRecordService;
import com.gzx.plugin.tjzy.modular.sms.reply.entity.GzxSmsReply;
import com.gzx.plugin.tjzy.modular.sms.reply.service.GzxSmsReplyService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import vip.xiaonuo.common.consts.GzxTenantConstant;
import vip.xiaonuo.common.tenant.TenantContext;
import vip.xiaonuo.common.util.CommonCryptogramUtil;
import vip.xiaonuo.common.util.CommonDateUtil;

import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * @author zjt
 * @description
 * @date 2025/7/21 13:31
 */
@Slf4j
@RequiredArgsConstructor
public abstract class SmsTemplateMethod {

    private final GzxSmsDbService smsDbService;

    private final GzxSmsRecordService smsRecordService;

    private final GzxSmsReceiptService smsReceiptService;

    private final GzxFoundsFlowService foundsFlowService;

    private final GzxSmsReplyService smsReplyService;

    /**
     * 获取当前供应商
     *
     * @return
     */
    protected abstract String getCurrentSupplierCode();

    /**
     * 获取短信签名
     *
     * @param city
     * @return
     */
    protected abstract String getSignName(String city);

    /**
     * 修改短信内容
     *
     * @param signCity 签名城市
     * @param content  短信内容
     * @return
     */
    public String modifySmsSignature(String signCity, String content) {
        if (content.startsWith("【")) {
            content = content.replaceFirst("【[^】]*】", "");
        }
        return content + "，拒收请回复R";
    }

    /**
     * 发送短信
     *
     * @param smsDetailList
     */
    public void sendProcess(List<GzxSmsDetail> smsDetailList) {
        List<CommonSmsSendReqDto> commonSmsSendReqDtoList = smsDetailList.stream().map(smsDetail -> new CommonSmsSendReqDto(smsDetail.getToPhone(), smsDetail.getSignCity(), smsDetail.getMessageContent(), smsDetail.getSupplierOrder())).toList();
        List<CommonSmsSendRespDto> commonSmsSendRespDtoList = startSend(commonSmsSendReqDtoList);
        handleSendAfter(smsDetailList, commonSmsSendRespDtoList);
    }

    /**
     * 获取短信发送状态
     *
     * @return
     */
    public void syncReceiptProcess() {
        List<GzxSmsReceipt> smsReceiptList = startSyncReceipt();
        if (CollectionUtil.isNotEmpty(smsReceiptList)) {
            //补全数据
            completionTenantForReceipt(smsReceiptList);
            //保存短信回执
            saveSMSReceipt(smsReceiptList);
            //解冻并消费项目预算
            unfreeAndConsume(smsReceiptList);
            //修改短信发送明细状态
            if (isMsgIdGroup()) {
                modifySmsDetailStatusGroup(smsReceiptList);
            } else {
                modifySmsDetailStatusNoGroup(smsReceiptList);
            }
        }
    }

    /**
     * 同步短信回复
     */
    public void syncReplyProcess() {
        List<GzxSmsReply> smsReplyList = startSyncReply();
        if (CollectionUtil.isNotEmpty(smsReplyList)) {
            completionTenantForReply(smsReplyList);
            smsReplyService.insertBatch(smsReplyList);
        }
    }

    /**
     * 短信余额提醒
     */
    public void balanceWarn() {

    }

    /**
     * 开始发送短信
     *
     * @param commonSmsSendReqDtoList 发送短信的集合
     * @return
     */
    protected abstract List<CommonSmsSendRespDto> startSend(List<CommonSmsSendReqDto> commonSmsSendReqDtoList);

    /**
     * 短信发送之后的默认响应
     *
     * @param commonSmsSendReqDtoList
     * @param status
     * @param supplierCode
     * @return
     */
    protected List<CommonSmsSendRespDto> defaultResp(List<CommonSmsSendReqDto> commonSmsSendReqDtoList, Integer status, String supplierCode) {
        List<CommonSmsSendRespDto> commonSmsSendRespDtoList = new ArrayList<>();
        for (CommonSmsSendReqDto commonSmsSendReqDto : commonSmsSendReqDtoList) {
            CommonSmsSendRespDto commonSmsSendRespDto = new CommonSmsSendRespDto();
            commonSmsSendRespDto.setMobile(commonSmsSendReqDto.getPhone());
            commonSmsSendRespDto.setResult(status);
            if (StringUtils.isNotBlank(commonSmsSendReqDto.getSupplierOrder()) && Objects.equals(status, GzxSmsSendStatusEnum.FAIL.getCode()) && commonSmsSendReqDto.getSupplierOrder().endsWith(supplierCode)) {
                commonSmsSendRespDto.setResult(GzxSmsSendStatusEnum.FINAL_FAIL.getCode());
            }
            commonSmsSendRespDtoList.add(commonSmsSendRespDto);
        }
        return commonSmsSendRespDtoList;
    }

    /**
     * 短信发送之后的回调
     *
     * @param smsDetailList
     * @param commonSmsSendRespDtoList
     */
    protected void handleSendAfter(List<GzxSmsDetail> smsDetailList, List<CommonSmsSendRespDto> commonSmsSendRespDtoList) {
        if (CollectionUtil.isNotEmpty(commonSmsSendRespDtoList)) {
            String supplierCode = getCurrentSupplierCode();
            String currentTime = CommonDateUtil.getCurrentTimeStr();
            Thread.startVirtualThread(() -> {
                Map<String, CommonSmsSendRespDto> respMap = commonSmsSendRespDtoList.stream().collect(Collectors.toMap(
                        CommonSmsSendRespDto::getMobile,
                        Function.identity(),
                        (existing, replacement) -> existing));
                for (GzxSmsDetail smsDetail : smsDetailList) {
                    CommonSmsSendRespDto commonSmsSendRespDto = respMap.get(smsDetail.getToPhone());
                    smsDetail.setMessageId(commonSmsSendRespDto.getMid());
                    smsDetail.setSupplierCode(supplierCode);
                    smsDetail.setSendStatus(GzxSmsSendStatusEnum.SENDING.getCode());
                    smsDetail.setSendTime(currentTime);
                    if (commonSmsSendRespDto.getResult() == 0) {
                        smsDetail.setSendStatus(GzxSmsSendStatusEnum.SENDING.getCode());
                    } else if (commonSmsSendRespDto.getResult().equals(GzxSmsSendStatusEnum.TIME_OUT.getCode())) {
                        smsDetail.setSendStatus(GzxSmsSendStatusEnum.TIME_OUT.getCode());
                    } else if (StringUtils.isNotBlank(smsDetail.getSupplierOrder()) && StringUtils.isNotBlank(smsDetail.getSupplierOrder()) && smsDetail.getSupplierOrder().endsWith(smsDetail.getSupplierCode())) {
                        smsDetail.setSendStatus(GzxSmsSendStatusEnum.FINAL_FAIL.getCode());
                    } else {
                        smsDetail.setSendStatus(GzxSmsSendStatusEnum.FAIL.getCode());
                    }
                }
                TenantContext.ignoreHandle(() -> {
                    smsDbService.saveMsgId(smsDetailList);
                    saveSmsRecord(supplierCode, smsDetailList);
                });
            });
        }
    }

    /**
     * 保存短信发送记录
     *
     * @param supplierCode
     * @param smsDetailList
     */
    protected void saveSmsRecord(String supplierCode, List<GzxSmsDetail> smsDetailList) {
        Date currentDate = new Date();
        List<GzxSmsRecord> smsRecordList = new ArrayList<>();
        for (GzxSmsDetail smsDetail : smsDetailList) {
            GzxSmsRecord smsRecord = new GzxSmsRecord();
            smsRecord.setSendType(GzxSmsSendTypeEnum.SYSTEM.getCode());
            smsRecord.setSupplierOrder(smsDetail.getSupplierOrder());
            smsRecord.setSupplierCode(supplierCode);
            smsRecord.setProjectCode(smsDetail.getProjectCode());
            smsRecord.setBatchNumber(smsDetail.getBatchNumber());
            smsRecord.setPhone(CommonCryptogramUtil.doSm1AesEncrypt(smsDetail.getToPhone()));
            smsRecord.setMsgId(smsDetail.getMessageId());
            smsRecord.setContent(smsDetail.getMessageContent());
            smsRecord.setTenantId(smsDetail.getTenantId());
            smsRecord.setCreateTime(currentDate);
            smsRecordList.add(smsRecord);
        }
        smsRecordService.insertBatch(smsRecordList);
    }

    /**
     * 同步短信回执
     *
     * @return
     */
    protected abstract List<GzxSmsReceipt> startSyncReceipt();

    /**
     * 开始同步短信回复
     *
     * @return
     */
    protected abstract List<GzxSmsReply> startSyncReply();

    /**
     * 短信是否发送成功
     *
     * @param resultCode
     * @return
     */
    protected abstract boolean isSuccess(String resultCode);

    /**
     * 是否需要根据短信ID分组
     *
     * @return
     */
    protected abstract boolean isMsgIdGroup();

    private void saveSMSReceipt(List<GzxSmsReceipt> smsReceiptList) {
        Thread.startVirtualThread(() -> TenantContext.ignoreHandle(() -> smsReceiptService.saveSMSReceipt(smsReceiptList)));
    }

    private void completionTenantForReceipt(List<GzxSmsReceipt> smsReceiptList) {
        Set<String> msgIdSet = smsReceiptList.stream().map(GzxSmsReceipt::getMsgId).collect(Collectors.toSet());
        List<GzxSmsRecord> smsRecordList = smsRecordService.listByWrapper(Wrappers.<GzxSmsRecord>lambdaQuery().in(GzxSmsRecord::getMsgId, msgIdSet));
        Map<String, GzxSmsRecord> msgMap = smsRecordList.stream().collect(Collectors.toMap(s -> isMsgIdGroup() ? s.getMsgId() + s.getPhone() : s.getMsgId(), Function.identity()));
        for (GzxSmsReceipt smsReceipt : smsReceiptList) {
            String key = isMsgIdGroup() ? smsReceipt.getMsgId() + smsReceipt.getPhone() : smsReceipt.getMsgId();
            if (msgMap.containsKey(key)) {
                GzxSmsRecord smsRecord = msgMap.get(key);
                smsReceipt.setTenantId(smsRecord.getTenantId());
                smsReceipt.setProjectCode(smsRecord.getProjectCode());
                smsReceipt.setBatchNumber(smsRecord.getBatchNumber());
                smsReceipt.setSupplierOrder(smsRecord.getSupplierOrder());
            } else {
                //根据手机号查询短信发送明细
                List<GzxSmsDetail> smsDetailList = smsDbService.listDetail(Wrappers.<GzxSmsDetail>lambdaQuery()
                        .eq(GzxSmsDetail::getSupplierCode, getCurrentSupplierCode())
                        .eq(GzxSmsDetail::getSendStatus, GzxSmsSendStatusEnum.TIME_OUT)
                        .eq(GzxSmsDetail::getToPhone, smsReceipt.getPhone())
                        .orderByDesc(GzxSmsDetail::getId));
                if (CollectionUtil.isNotEmpty(smsDetailList)) {
                    GzxSmsDetail smsDetail = smsDetailList.getFirst();
                    smsReceipt.setTenantId(smsDetail.getTenantId());
                    smsReceipt.setProjectCode(smsDetail.getProjectCode());
                    smsReceipt.setBatchNumber(smsDetail.getBatchNumber());
                    smsReceipt.setSupplierOrder(smsDetail.getSupplierOrder());
                } else {
                    smsReceipt.setTenantId(GzxTenantConstant.GZX_TENANT_ID);
                    smsReceipt.setProjectCode("OTHER");
                }
            }
        }
    }

    private void completionTenantForReply(List<GzxSmsReply> smsReplyList) {
        Set<String> msgIdSet = smsReplyList.stream().map(GzxSmsReply::getMsgId).collect(Collectors.toSet());
        List<GzxSmsRecord> smsRecordList = smsRecordService.listByWrapper(Wrappers.<GzxSmsRecord>lambdaQuery().in(GzxSmsRecord::getMsgId, msgIdSet));
        Map<String, GzxSmsRecord> msgMap = smsRecordList.stream().collect(Collectors.toMap(s -> isMsgIdGroup() ? s.getMsgId() + s.getPhone() : s.getMsgId(), Function.identity()));
        for (GzxSmsReply smsReply : smsReplyList) {
            String key = isMsgIdGroup() ? smsReply.getMsgId() + smsReply.getPhone() : smsReply.getMsgId();
            if (msgMap.containsKey(key)) {
                GzxSmsRecord smsRecord = msgMap.get(key);
                smsReply.setSendContent(smsRecord.getContent());
                smsReply.setSendTime(CommonDateUtil.dateTimeToStr(smsRecord.getCreateTime()));
                smsReply.setTenantId(smsRecord.getTenantId());
                smsReply.setProjectCode(smsRecord.getProjectCode());
                smsReply.setBatchNumber(smsRecord.getBatchNumber());
            }
        }
    }

    private void unfreeAndConsume(List<GzxSmsReceipt> smsReceiptList) {
        Map<String, Map<String, Map<String, Integer>>> countMap = getSuccessCount(smsReceiptList);
        for (Map.Entry<String, Map<String, Map<String, Integer>>> tenantEntry : countMap.entrySet()) {
            //租户ID
            String tenantId = tenantEntry.getKey();
            for (Map.Entry<String, Map<String, Integer>> projectEntry : tenantEntry.getValue().entrySet()) {
                //项目
                String projectCode = projectEntry.getKey();
                for (Map.Entry<String, Integer> entry : projectEntry.getValue().entrySet()) {
                    GzxTransMethodBo transMethodBo = new GzxTransMethodBo();
                    transMethodBo.setSupplierCode(getCurrentSupplierCode());
                    transMethodBo.setProjectCode(projectCode);
                    transMethodBo.setTenantId(tenantId);
                    transMethodBo.setFoundsType(FoundsConstant.FOUNDS_TYPE_SMS);
                    //解冻
                    if ("total".equals(entry.getKey())) {
                        transMethodBo.setRowNum(entry.getValue());
                        transMethodBo.setTransType(FoundsConstant.TRANS_TYPE_UNFREEZE);
                        foundsFlowService.transUnfreeze(transMethodBo);
                    }
                    //扣减
                    if ("success".equals(entry.getKey())) {
                        transMethodBo.setRowNum(entry.getValue());
                        transMethodBo.setTransType(FoundsConstant.TRANS_TYPE_CONSUME);
                        foundsFlowService.transConsume(transMethodBo);
                    }
                }
            }
        }
    }


    //租户、项目、成功数And总数
    private Map<String, Map<String, Map<String, Integer>>> getSuccessCount(List<GzxSmsReceipt> smsRecordList) {
        return smsRecordList.stream().collect(
                Collectors.groupingBy(GzxSmsReceipt::getTenantId,
                        Collectors.groupingBy(GzxSmsReceipt::getProjectCode,
                                Collectors.collectingAndThen(Collectors.toList(), list -> {
                                    int total = (int) list.stream()
                                            .map(r -> r.getMsgId() + r.getPhone())
                                            .distinct()
                                            .count();
                                    int success = (int) list.stream()
                                            .filter(r -> r.getMappingStatus() == 1)
                                            .count();
                                    return Map.of(
                                            "success", success,
                                            "total", total
                                    );
                                })
                        )
                )
        );
    }

    protected void modifySmsDetailStatusGroup(List<GzxSmsReceipt> smsReceiptList) {
        Map<String, List<GzxSmsReceipt>> smsReceiptMap = smsReceiptList.stream().collect(Collectors.groupingBy(GzxSmsReceipt::getMsgId));
        for (Map.Entry<String, List<GzxSmsReceipt>> entry : smsReceiptMap.entrySet()) {
            List<String> successList = new ArrayList<>();
            List<String> failList = new ArrayList<>();
            List<String> finalFailList = new ArrayList<>();
            for (GzxSmsReceipt smsReceipt : entry.getValue()) {
                if (isSuccess(smsReceipt.getResultCode())) {
                    successList.add(smsReceipt.getPhone());
                } else if (StringUtils.isNotBlank(smsReceipt.getSupplierOrder()) && smsReceipt.getSupplierOrder().endsWith(smsReceipt.getSupplierCode())) {
                    finalFailList.add(smsReceipt.getPhone());
                } else {
                    failList.add(smsReceipt.getPhone());
                }
            }
            if (CollectionUtil.isNotEmpty(successList)) {
                Thread.startVirtualThread(() -> TenantContext.ignoreHandle(() -> smsDbService.updateDetail(Wrappers.<GzxSmsDetail>lambdaUpdate()
                        .set(GzxSmsDetail::getSendStatus, GzxSmsSendStatusEnum.SUCCESS.getCode())
                        .eq(GzxSmsDetail::getMessageId, entry.getKey())
                        .in(GzxSmsDetail::getToPhone, successList))));
            }
            if (CollectionUtil.isNotEmpty(failList)) {
                Thread.startVirtualThread(() -> TenantContext.ignoreHandle(() -> smsDbService.updateDetail(Wrappers.<GzxSmsDetail>lambdaUpdate()
                        .set(GzxSmsDetail::getSendStatus, GzxSmsSendStatusEnum.FAIL.getCode())
                        .eq(GzxSmsDetail::getMessageId, entry.getKey())
                        .in(GzxSmsDetail::getToPhone, failList))));
            }
            if (CollectionUtil.isNotEmpty(finalFailList)) {
                Thread.startVirtualThread(() -> TenantContext.ignoreHandle(() -> smsDbService.updateDetail(Wrappers.<GzxSmsDetail>lambdaUpdate()
                        .set(GzxSmsDetail::getSendStatus, GzxSmsSendStatusEnum.FINAL_FAIL.getCode())
                        .eq(GzxSmsDetail::getMessageId, entry.getKey())
                        .in(GzxSmsDetail::getToPhone, finalFailList))));
            }
        }
    }

    protected void modifySmsDetailStatusNoGroup(List<GzxSmsReceipt> smsReceiptList) {
        List<String> successList = new ArrayList<>();
        List<String> failList = new ArrayList<>();
        List<String> finalFailList = new ArrayList<>();
        for (GzxSmsReceipt smsReceipt : smsReceiptList) {
            if (isSuccess(smsReceipt.getResultCode())) {
                successList.add(smsReceipt.getMsgId());
            } else if (StringUtils.isNotBlank(smsReceipt.getSupplierOrder()) && smsReceipt.getSupplierOrder().endsWith(smsReceipt.getSupplierCode())) {
                finalFailList.add(smsReceipt.getMsgId());
            } else {
                failList.add(smsReceipt.getMsgId());
            }
        }
        if (CollectionUtil.isNotEmpty(successList)) {
            Thread.startVirtualThread(() -> TenantContext.ignoreHandle(() -> smsDbService.updateDetail(Wrappers.<GzxSmsDetail>lambdaUpdate()
                    .set(GzxSmsDetail::getSendStatus, GzxSmsSendStatusEnum.SUCCESS.getCode())
                    .in(GzxSmsDetail::getMessageId, successList))));
        }
        if (CollectionUtil.isNotEmpty(failList)) {
            Thread.startVirtualThread(() -> TenantContext.ignoreHandle(() -> smsDbService.updateDetail(Wrappers.<GzxSmsDetail>lambdaUpdate()
                    .set(GzxSmsDetail::getSendStatus, GzxSmsSendStatusEnum.FAIL.getCode())
                    .in(GzxSmsDetail::getMessageId, failList))));
        }
        if (CollectionUtil.isNotEmpty(finalFailList)) {
            Thread.startVirtualThread(() -> TenantContext.ignoreHandle(() -> smsDbService.updateDetail(Wrappers.<GzxSmsDetail>lambdaUpdate()
                    .set(GzxSmsDetail::getSendStatus, GzxSmsSendStatusEnum.FINAL_FAIL.getCode())
                    .in(GzxSmsDetail::getMessageId, finalFailList))));
        }
    }
}
